home *** CD-ROM | disk | FTP | other *** search
/ Nebula 2 / Nebula Two.iso / SourceCode / Palettes / PAThumbWheel / PAThumbWheelCell.m < prev    next >
Text File  |  1995-06-12  |  15KB  |  353 lines

  1. #import "PAThumbWheelCell.h"
  2. #import "PAThumbWheelCellDrawing.h"
  3. #import "PAThumbWheel.h"
  4.  
  5. /******************************************************************************
  6.     PAThumbWheelCell
  7.     
  8. PAThumbWheel offers the functionality of Slider plus the features that you would expect from a real thumbwheel (including 2 3/4 D Graphics!).
  9.  
  10. PAThumbWheel has a linear display mode and a radial display mode and offers the ability to assign a value to the visible region of the control as well as an absolute value that the ThumbWheel will either ignore, bound to or wrap around.
  11.  
  12. PAThumbWheel can also return relative values via its -relativeIntValue & -relativeFloatValue methods. A snap back option allows mouse loops to start from and return to a base value.
  13.  
  14. Copyright 1992, Jeff Martin. (jmartin@next.com 415-780-3833)
  15. ******************************************************************************/
  16. #define PI            3.141592654
  17. #define HALF_PI        1.570796327
  18. #define DEG_TO_RAD 0.0174532925199433        //  PI/180.0
  19. #define SIN(x) (sin(DEG_TO_RAD*(x)))
  20. #define COS(x) (cos(DEG_TO_RAD*(x)))
  21. #define TAN(x) (tan(DEG_TO_RAD*(x)))
  22.  
  23. #define RAD_TO_DEG 57.295779513082320876    //  180.0/PI
  24. #define ASIN(x) (asin(x)*RAD_TO_DEG)
  25. #define ACOS(x) (acos(x)*RAD_TO_DEG)
  26. #define ATAN(x) (atan(x)*RAD_TO_DEG)
  27. // A mod function for floating values
  28. #define MOD(x,y) ((x) - (y)*(int)((float)(x)/(y)))
  29. // Keep 'a' between x and y
  30. #define CLAMP(a,x,y) (MAX((x), MIN((y), (a))))
  31. // Keep 'a' between 'x' and 'y' by wrapping it to the other side
  32. #define CLAMP_WITH_WRAP(a,x,y)                                              \
  33. ( ((a) < (x)) ? ((y) - MOD(((x)-(a)),((y)-(x)))) :                         \
  34. ( ((a) > (y)) ? ((x) + MOD(((a)-(y)),((y)-(x)))) : (a) ) )
  35. // Is a between x and y
  36. #define ISBETWEEN(a,x,y) (((a)>=(x))&&((a)<=(y)))
  37. #define EQUAL(a,b) (ABS((a)-(b))<0.00001)
  38.  
  39. @implementation PAThumbWheelCell
  40.  
  41. // Set reasonable defaults
  42. - init
  43. {
  44.     [super init];
  45.     [self setType:NX_TEXTCELL];
  46.     [self setHorizontal];
  47.  
  48.     [self setLinear]; [self setUnbounded];
  49.     [self setVisibleMax:1.0]; [self setVisibleMin:-1.0];
  50.     [self setAbsoluteMax:2.0]; [self setAbsoluteMin:-2.0];
  51.     [self setSnapsBack:YES]; [self setSnapBackValue:0.0];
  52.     [self setDashInterval:10]; [self setShowMainDash:YES];
  53.     [self setColor:NX_COLORLTGRAY];
  54.     
  55.     [self sendActionOn:NX_MOUSEDOWNMASK|NX_MOUSEDRAGGEDMASK|NX_MOUSEUPMASK];
  56.     return self;
  57. }
  58.  
  59. // Override trackMouse to provide an infinitely large area(continuous tracking)
  60. - (BOOL)trackMouse:(NXEvent *)event inRect:(const NXRect *)rect ofView:view
  61. { cellFrame = *rect; return [super trackMouse:event inRect:NULL ofView:view]; }
  62.  
  63. // Override to return YES (always track mouse)
  64. - (BOOL)startTrackingAt:(const NXPoint *)startPoint inView:aView { return YES;}
  65.  
  66. // Override from cell to track mouse
  67. - (BOOL)continueTracking:(const NXPoint *)lastPoint 
  68.     at:(const NXPoint *)currPoint inView:view
  69. {
  70.     float currPointVal =[self floatValueAtPoint:*currPoint forFrame:cellFrame];
  71.     float lastPointVal =[self floatValueAtPoint:*lastPoint forFrame:cellFrame];
  72.     float mouseScale = ([NXApp currentEvent]->flags&NX_ALTERNATEMASK)? .1 : 1;
  73.     
  74.     // If TW is bounded and at absolute bound, only track when mouse comes back
  75.     if([self isBounded]) {
  76.         // Return if we are already at absoluteMax and currPoint is greater
  77.         if(EQUAL([self floatValue], [self absoluteMax])) {
  78.             if(currPointVal > [self absoluteMax]) return YES;
  79.             else lastPointVal = [self absoluteMax];
  80.         }
  81.         // Return if we are already at absoluteMin and currPoint is less
  82.         if(EQUAL([self floatValue], [self absoluteMin])) {
  83.             if(currPointVal < [self absoluteMin]) return YES;
  84.             else lastPointVal = [self absoluteMin];
  85.         }
  86.     }
  87.     
  88.     // Set the float value relative to last point
  89.     [self setFloatValue:[self floatValue] + 
  90.         (currPointVal - lastPointVal)*mouseScale];
  91.     
  92.     // Redraw control
  93.     [view display];
  94.     return YES;    // Always continue to track mouse
  95. }
  96.  
  97.  
  98. /******************************************************************************
  99.     floatValueAtPoint:forFrame:
  100.     
  101.     This method gives the value that corresponds to a point with respect to the given frame and the visible range. When in radial mode, the point on the thumbwheel is approximated with a power series for arcCos to get legal values for points outside of the frame.
  102. ******************************************************************************/
  103. - (float)floatValueAtPoint:(NXPoint)point forFrame:(NXRect)frame
  104. {
  105.     float pos = [self isVertical] ? point.y : point.x;
  106.     float base = [self isVertical] ? NX_Y(&frame) : NX_X(&frame);
  107.     float width = [self isVertical] ? NX_HEIGHT(&frame) : NX_WIDTH(&frame);
  108.     float value;
  109.     
  110.     if([self isLinear])
  111.         value = [self visibleMin] + (pos - base)/width*[self visibleRange];
  112.     else {
  113.         float radius = width/2;
  114.         float midP = base + radius;
  115.         float x = (midP - pos)/radius;
  116.         // Get degrees by pwr series approximation of ArcCos (Pi/2 - x - x^3/6)
  117.         float alpha = (HALF_PI - x - x*x*x/6);
  118.         // Convert degrees to TW coords
  119.         value = [self visibleMin] + alpha/PI*[self visibleRange];
  120.     }
  121.     return value;
  122. }
  123.     
  124. // Override from cell to snapback if neccessary
  125. - stopTracking:(const NXPoint *)lastPoint at:(const NXPoint *)stopPoint
  126.     inView:view mouseIsUp:(BOOL)flag
  127. {
  128.     // If the ThumbWheel is in snap back mode, snap it back and redraw
  129.     if([self snapsBack]) {
  130.         [self setFloatValue:[self snapBackValue]];
  131.         [self resetRelativeValue];
  132.         [view display];
  133.     }
  134.     return [super stopTracking:lastPoint at:stopPoint inView:view 
  135.         mouseIsUp:flag];
  136. }
  137.  
  138. // Direction is either DIRECTION_HORIZONTAL or DIRECTION_VERTICAL
  139. - (int)direction { return direction; }
  140. - setDirection:(int)dir
  141. { direction = dir; [image free]; image = NULL; return self; }
  142. - (BOOL)isVertical { return direction == DIRECTION_VERTICAL; }
  143. - setVertical { direction = DIRECTION_VERTICAL; return self; }
  144. - (BOOL)isHorizontal { return direction == DIRECTION_HORIZONTAL; }
  145. - setHorizontal { direction = DIRECTION_HORIZONTAL; return self; }
  146.  
  147. /******************************************************************************
  148.     displayMode, setDisplayMode
  149.     
  150.     The displayMode is either DISPLAY_MODE_LINEAR or DISPLAY_MODE_RADIAL. Linear displays a flat ruler type control whereas radial displays a 3D thumbwheel that actually looks curved .
  151. ******************************************************************************/
  152. - (int)displayMode { return displayMode; }
  153. - setDisplayMode:(int)mode { displayMode = mode; return self; }
  154. - (BOOL)isRadial { return displayMode == DISPLAY_MODE_RADIAL; }
  155. - setRadial { return [self setDisplayMode:DISPLAY_MODE_RADIAL]; }
  156. - (BOOL)isLinear { return displayMode == DISPLAY_MODE_LINEAR; }
  157. - setLinear { return [self setDisplayMode:DISPLAY_MODE_LINEAR]; }
  158.  
  159. /******************************************************************************
  160.     intValue, setIntValue, floatValue, setFloatValue
  161.     
  162.     These methods are overridden to allow us to calculate relative values and to constrain the value with respect to the absolute mode and absolute values.
  163. ******************************************************************************/
  164. - (int)intValue { return (int)[self floatValue]; }
  165. - setIntValue:(int)val { [self setFloatValue:val]; return self; }
  166. - (float)floatValue { return floatValue; }
  167. - setFloatValue:(float)val
  168.     // Clamp or Wrap newValue wrt the absoluteMode
  169.     if(!ISBETWEEN(val, [self absoluteMin], [self absoluteMax])) {
  170.         if([self isBounded]) val = 
  171.             CLAMP(val, [self absoluteMin], [self absoluteMax]);
  172.         else if([self isWrapped]) val = 
  173.             CLAMP_WITH_WRAP(val, [self absoluteMin], [self absoluteMax]);
  174.     }
  175.  
  176.     // Store last float value and set float value
  177.     lastFloatValue = floatValue; floatValue = val;
  178.     return self;
  179. }
  180. - (float)lastFloatValue { return lastFloatValue; }
  181. - setLastFloatValue:(float)value { lastFloatValue=value; return self;}
  182.  
  183. /******************************************************************************
  184.     visibleMax, visibleMin, visibleRange, middleValue
  185.     
  186.     VisibleMax and visibleMin are the values of the thumbwheel at either end; max value is at right/top, min is at left/bottom. middleValue is the value of the TW at the center (wrt visibleMin and visibleMax).
  187. ******************************************************************************/
  188. - (float)visibleMax { return visMax; }
  189. - setVisibleMax:(float)max { visMax = max; return self; }
  190. - (float)visibleMin { return visMin; }
  191. - setVisibleMin:(float)min { visMin = min; return self; }
  192. - (float)visibleRange { return [self visibleMax] - [self visibleMin]; }
  193. - (float)middleValue { return [self visibleMin] + [self visibleRange]/2; }
  194.  
  195. /******************************************************************************
  196.     absoluteMode, absoluteMax, absoluteMin
  197.     
  198.     The absolute mode refers to ThumbWheel values that exceed the visible range. ABSOLUTE_UNBOUNDED means that the TW can be dragged as high or low as desired. ABSOLUTE_BOUNDED means that the TW will be clamped to some arbitrarily large value. ABSOLUTE_WRAPPED means that the TW will wrap from the absoluteMax to the absoluteMin (and vise-versa) when applicable. AbsoluteMax is the value off to the right and up. AbsoluteMin us the value off to the left and down.
  199. ******************************************************************************/
  200. - (int)absoluteMode        { return absMode; }
  201. - setAbsoluteMode:(int)mode { absMode = mode; return self; }
  202. - (BOOL)isUnbounded        { return absMode == ABSOLUTE_UNBOUNDED;  }
  203. - setUnbounded            { return [self setAbsoluteMode:ABSOLUTE_UNBOUNDED]; }
  204. - (BOOL)isBounded         { return absMode == ABSOLUTE_BOUNDED; }
  205. - setBounded            { return [self setAbsoluteMode:ABSOLUTE_BOUNDED]; }
  206. - (BOOL)isWrapped        { return absMode == ABSOLUTE_WRAPPED; }
  207. - setWrapped            { return [self setAbsoluteMode:ABSOLUTE_WRAPPED]; }
  208. - (float)absoluteMax    { return absMax; }
  209. - setAbsoluteMax:(float)value { absMax = value; return self; }
  210. - (float)absoluteMin     { return absMin; }
  211. - setAbsoluteMin:(float)value { absMin = value; return self; }
  212. - (float)absoluteRange    { return [self absoluteMax] - [self absoluteMin]; }
  213.  
  214. /******************************************************************************
  215.     relativeIntValue, relativeFloatValue, resetRelativeValue
  216.     
  217.     These two methods return the change of the value since the last iteration. This is useful for a relative method call ( rotateBy: as opposed to rotateTo:). resetRelativeValue sets the relative change to zero (typically only called internally when snapping back).
  218. ******************************************************************************/
  219. - (int)relativeIntValue
  220. { return [self intValue] - (int)[self lastFloatValue];}
  221. - (float)relativeFloatValue
  222. { return [self floatValue] - [self lastFloatValue]; }
  223. - resetRelativeValue { [self setLastFloatValue:[self floatValue]];return self;}
  224.  
  225. /******************************************************************************
  226.     snapsBack, snapBackValue
  227.     
  228.     It is sometimes useful to have the thumbwheel snap back to some value (zero by default) so that it can be used for relative modification of values (rotate by as opposed to rotateTo:).
  229. ******************************************************************************/
  230. - (BOOL)snapsBack                { return snapsBack; }
  231. - setSnapsBack:(BOOL)flag        { snapsBack = flag; return self; }
  232. - (float)snapBackValue            {return snapBackValue; }
  233. - setSnapBackValue:(float)value    { snapBackValue = value;return self;}
  234.  
  235. /******************************************************************************
  236.     dashInterval, setDashInterval, setDashIntervalEqual
  237.  
  238.      The dash interval is either in degrees (DISPLAY_MODE_RADIAL) or PostScript 
  239. points (DISPLAY_MODE_LINEAR). The default is 10 of each.
  240. ******************************************************************************/
  241. - (float)dashInterval            { return dashInterval; }
  242. - setDashInterval:(float)val    { dashInterval = val; return self; }
  243.  
  244. /******************************************************************************
  245.     showMainDash, setShowMainDash
  246.  
  247.      The main dash is the dash in the center of the control and gives feedback as to the absolute value of the control. This should be set to NO for TW that only provide relative values.
  248. ******************************************************************************/
  249. - (BOOL)showMainDash             {return showMainDash;}
  250. - setShowMainDash:(BOOL)flag    { showMainDash = flag; return self; }
  251.  
  252. /******************************************************************************
  253.     color, setColor
  254.  
  255.      These methods manipulate the predominant color of the ThumbWheel. Radial ThumbWheel are of course shades of these. The default is NX_COLORLTGRAY. 
  256. ******************************************************************************/
  257. - (NXColor)color            {return color;}
  258. - setColor:(NXColor)c        { color = c; return self; }
  259.  
  260. /******************************************************************************
  261.     shift
  262.     
  263.      The shift is how much the dashes are shifted by to achieve the animation of motion it is in points. It is calculated from the visibleRange and the physicalRange (frame).
  264. ******************************************************************************/
  265. - (int)shift:(const NXRect *)frame
  266. {    
  267.     if([self isLinear]) {
  268.         if([self isHorizontal])
  269.             return (int)(([self floatValue] - [self visibleMin]) / 
  270.                 [self visibleRange]*NX_WIDTH(frame) + .5);
  271.         else
  272.             return (int)(([self floatValue] - [self visibleMin]) / 
  273.                 [self visibleRange]*NX_HEIGHT(frame) +.5);
  274.     }
  275.     else return (int)(([self floatValue] - [self visibleMin])/ 
  276.         [self visibleRange]*180 + .5);
  277. }
  278.  
  279. // Override highlight:inView:lit: so that it does nothing
  280. - highlight:(const NXRect *)frame inView:view lit:(BOOL)flag { return self; }
  281.  
  282. // Override this so that we track mouse whether or not it is on top of us.
  283. + (BOOL)prefersTrackingUntilMouseUp { return YES; }
  284.  
  285.  
  286.  
  287. /******************************************************************************
  288.  
  289.      The Read and Write Methods are for archival.
  290. ******************************************************************************/
  291. - write:(NXTypedStream *)stream
  292. {
  293.     [super write:stream];
  294.  
  295.     NXWriteType(stream, "i", &displayMode);
  296.     NXWriteType(stream, "i", &direction);
  297.  
  298.     NXWriteType(stream, "f", &floatValue);
  299.     NXWriteType(stream, "f", &lastFloatValue);
  300.  
  301.     NXWriteType(stream, "f", &visMax);
  302.     NXWriteType(stream, "f", &visMin);
  303.  
  304.     NXWriteType(stream, "i", &absMode);
  305.     NXWriteType(stream, "f", &absMax);
  306.     NXWriteType(stream, "f", &absMin);
  307.  
  308.     NXWriteType(stream, "c", &snapsBack);
  309.     NXWriteType(stream, "f", &snapBackValue);
  310.  
  311.     NXWriteType(stream, "i", &dashInterval);
  312.     NXWriteType(stream, "c", &showMainDash);
  313.  
  314.     NXWriteColor(stream, color);
  315.     return self;
  316. }
  317.  
  318. - read:(NXTypedStream *)stream
  319. {
  320.     [super read:stream];
  321.  
  322.     NXReadType(stream, "i", &displayMode);
  323.     NXReadType(stream, "i", &direction);
  324.  
  325.  
  326.     NXReadType(stream, "f", &floatValue);
  327.     NXReadType(stream, "f", &lastFloatValue);
  328.  
  329.     NXReadType(stream, "f", &visMax);
  330.     NXReadType(stream, "f", &visMin);
  331.  
  332.     NXReadType(stream, "i", &absMode);
  333.     NXReadType(stream, "f", &absMax);
  334.     NXReadType(stream, "f", &absMin);
  335.  
  336.     NXReadType(stream, "c", &snapsBack);
  337.     NXReadType(stream, "f", &snapBackValue);
  338.  
  339.     NXReadType(stream, "i", &dashInterval);
  340.     NXReadType(stream, "c", &showMainDash);
  341.  
  342.     color = NXReadColor(stream);
  343.     return self;
  344. }
  345.  
  346. - free
  347. {
  348.     [image free];
  349.     return [super free];
  350. }
  351. @end
  352.